let PlotUtils = function () { };

/* ================== 依托Leaflet类库的方法开始 ======================== */

// 将地理坐标转为平面坐标（标绘时的位置计算全部基于平面坐标）
PlotUtils.prototype.proPoints = function (latlngs) {
    let proPts = [];
    for (let i = 0; i < latlngs.length; i++) {
        let proPt = this.proPoint(latlngs[i]) // 转为平面坐标            
        proPts.push({ x: proPt.x, y: proPt.y })
    }
    return proPts;
};

PlotUtils.prototype.proPoint = function (latlng) {
    return L.Projection.Mercator.project(latlng)// 转为平面坐标 ;
};

// 将平面坐标转为地理坐标（计算完成，转换坐标）
PlotUtils.prototype.unProPoints = function (proPts) {
    let latlngs = [];
    for (let i = 0; i < proPts.length; i++) {
        let pt = this.unProPoint(proPts[i]) // 转为地理坐标
        latlngs.push(pt)
    }
    return latlngs;
};

PlotUtils.prototype.unProPoint = function (proPt) {
    return L.Projection.Mercator.unproject(L.point(proPt)) // 转为平面坐标 ;
};

/* =================不依托Leaflet类库的方法结束 ======================== */

/**
 * 计算二次贝塞尔曲线控制点
 */
PlotUtils.prototype.calculateBezierControlPoints = function (p0, p1, p2) {
    // 计算中间控制点，使得曲线在t=0.5时经过p1
    const controlPoint = {
        x: 2 * p1.x - 0.5 * p0.x - 0.5 * p2.x,
        y: 2 * p1.y - 0.5 * p0.y - 0.5 * p2.y
    };

    // 返回二次贝塞尔曲线的三个控制点：起点、控制点、终点
    return [p0, controlPoint, p2];
}

/**
* 计算二次贝塞尔曲线上的点
* @param {Object} p0 - 起点坐标，格式 {x: number, y: number}
* @param {Object} p1 - 控制点坐标
* @param {Object} p2 - 终点坐标
* @param {number} t - 参数值（范围0-1）
* @returns {Object} 曲线上的坐标点 {x: number, y: number}
*/
PlotUtils.prototype.calculateBezierPoint = function (p0, p1, p2, t) {
    // 二次贝塞尔曲线公式：B(t) = (1-t)^2*P0 + 2*(1-t)t*P1 + t^2*P2
    const t1 = 1 - t;
    return {
        x: t1 * t1 * p0.x + 2 * t1 * t * p1.x + t * t * p2.x,
        y: t1 * t1 * p0.y + 2 * t1 * t * p1.y + t * t * p2.y
    };
}

/**
 * 生成通过三个点的贝塞尔曲线坐标集合
 * @param {Object[]} points - 三个坐标点数组，格式 [{x,y}, {x,y}, {x,y}]
 * @param {number} [step=0.01] - 曲线精度步长（默认0.01）
 * @returns {Object[]} 曲线点坐标数组
 */
PlotUtils.prototype.generateBezierCurve = function (points, step = 0.01) {
    if (points.length !== 3) throw new Error('需要三个点生成贝塞尔曲线');

    const [p0, p1, p2] = points;
    const curvePoints = [];

    for (let t = 0; t <= 1.0001; t += step) {
        curvePoints.push(this.calculateBezierPoint(p0, p1, p2, t));
    }

    return curvePoints;
}

/**
* 计算二次贝塞尔曲线在参数 t 处的切线方向向量
* @param {Object} p0 - 起点，格式 { x: number, y: number }
* @param {Object} p1 - 控制点，格式 { x: number, y: number }
* @param {Object} p2 - 终点，格式 { x: number, y: number }
* @param {number} t - 参数值，范围 [0, 1]
* @returns {Object} 切线方向向量 { x: number, y: number }
*/
PlotUtils.prototype.getQuadraticBezierPointAndTangent = function (p0, p1, p2, t) {
    // 计算点
    const x = (1 - t) ** 2 * p0.x + 2 * t * (1 - t) * p1.x + t ** 2 * p2.x;
    const y = (1 - t) ** 2 * p0.y + 2 * t * (1 - t) * p1.y + t ** 2 * p2.y;
    // 计算导数
    const dx = 2 * ((1 - t) * (p1.x - p0.x) + t * (p2.x - p1.x));
    const dy = 2 * ((1 - t) * (p1.y - p0.y) + t * (p2.y - p1.y));
    return { point: { x, y }, tangent: { x: dx, y: dy } };
}

/**
 * 计算三次贝塞尔曲线控制点
 */
PlotUtils.prototype.calculateCubicBezierControlPoints = function (points) {
    const p0 = points[0];
    const p1 = points[1];
    const p2 = points[2];
    const p3 = points[3];

    // 计算中间变量c1和c2的x分量
    const c1x = p1.x - (8 / 27) * p0.x - (1 / 27) * p3.x;
    const c2x = p2.x - (1 / 27) * p0.x - (8 / 27) * p3.x;

    // 解线性方程组得到控制点C1和C2的x坐标
    const C1x = 3 * c1x - 1.5 * c2x;
    const C2x = 3 * c2x - 1.5 * c1x;

    // 计算中间变量c1和c2的y分量
    const c1y = p1.y - (8 / 27) * p0.y - (1 / 27) * p3.y;
    const c2y = p2.y - (1 / 27) * p0.y - (8 / 27) * p3.y;

    // 解线性方程组得到控制点C1和C2的y坐标
    const C1y = 3 * c1y - 1.5 * c2y;
    const C2y = 3 * c2y - 1.5 * c1y;

    return [
        p0,
        { x: C1x, y: C1y },
        { x: C2x, y: C2y },
        p3
    ];
}

/**
 * 三次贝塞尔曲线生成方法
 * @param {Object} p0 - 起点坐标 {x, y}
 * @param {Object} p1 - 控制点1坐标 {x, y}
 * @param {Object} p2 - 控制点2坐标 {x, y}
 * @param {Object} p3 - 终点坐标 {x, y}
 * @param {number} [step=0.01] - 生成点的间隔精度（越小越密集）
 * @returns {Array} 贝塞尔曲线上的坐标点数组
 */
PlotUtils.prototype.cubicBezier = function (p0, p1, p2, p3, step = 0.01) {
    const points = [];

    for (let t = 0; t <= 1.0001; t += step) {
        const x = this.bezierFormula(t, p0.x, p1.x, p2.x, p3.x);
        const y = this.bezierFormula(t, p0.y, p1.y, p2.y, p3.y);
        points.push({ x, y });
    }

    return points;
}

// 三次贝塞尔曲线公式
PlotUtils.prototype.bezierFormula = function (t, a0, a1, a2, a3) {
    return (
        a0 * Math.pow(1 - t, 3) +
        3 * a1 * t * Math.pow(1 - t, 2) +
        3 * a2 * Math.pow(t, 2) * (1 - t) +
        a3 * Math.pow(t, 3)
    );
}

/**
 * 获取长度
 */
PlotUtils.prototype.distanceByT = function (points, t) {
    let distance = 0;
    for (let i = 0; i < t; i++) {
        distance += this.distance(points[i], points[i + 1]);
    }
    return distance;
};

/**
 * 获取所有点的总长度
 */
PlotUtils.prototype.totalDistance = function (points) {
    let distance = 0;
    for (let i = 0; i < points.length - 1; i++) {
        distance += this.distance(points[i], points[i + 1]);
    }
    return distance;
};

/**
 * 两点间平面距离
 */
PlotUtils.prototype.distance = function (pnt1, pnt2) {
    return Math.sqrt(Math.pow((pnt1.x - pnt2.x), 2) + Math.pow((pnt1.y - pnt2.y), 2));
}

/**
 * 根据点位查找t
 * 
 */
PlotUtils.prototype.findT = function (points, pnt) {
    let threshold = 0.001;
    let start = 0;
    let end = points.length - 1;
    let mid = parseInt(Math.floor((start + end) / 2));

    for (; ;) {
        let len1 = this.distance(points[start], pnt);
        let len2 = this.distance(pnt, points[mid]);
        let len3 = this.distance(pnt, points[end])

        if (len1 < threshold) {
            return 0;
        }
        if (len2 < threshold) {
            return mid / (points.length - 1);
        }
        if (len3 < threshold) {
            return 1;
        }

        if (len1 + len2 < len2 + len3) {
            end = mid;
            mid = parseInt(Math.floor((start + end) / 2));
        } else {
            start = mid;
            mid = parseInt(Math.floor((start + end) / 2));
        }

        if (start == mid) {
            break;
        }

    }
}

/**
 * 生成经过给定点的平滑曲线坐标点数组
 * @param {Array} points - 输入的坐标点数组，格式为 [{x: number, y: number}, ...]
 * @param {number} numSegments - 每两个原始点之间的曲线分段数，默认为25
 * @returns {Array} 平滑曲线的坐标点数组
 */
PlotUtils.prototype.createSmoothCurve = function (points, numSegments = 100) {
    if (points.length < 2) return points.slice();

    // 扩展点数组处理边界情况
    const extendedPoints = [
        points[1],          // 添加第二个点作为起始虚拟点
        ...points,          // 原始点
        points[points.length - 2] // 添加倒数第二个点作为结束虚拟点
    ];

    const curvePoints = [];

    // 遍历每个中间段生成曲线
    for (let i = 1; i <= extendedPoints.length - 3; i++) {
        const p0 = extendedPoints[i - 1];
        const p1 = extendedPoints[i];
        const p2 = extendedPoints[i + 1];
        const p3 = extendedPoints[i + 2];

        // 生成当前段的曲线点
        for (let t = 0; t < 1; t += 1 / numSegments) {
            const t2 = t * t;
            const t3 = t2 * t;

            // Catmull-Rom样条插值公式
            const x = 0.5 * (
                (2 * p1.x) +
                (-p0.x + p2.x) * t +
                (2 * p0.x - 5 * p1.x + 4 * p2.x - p3.x) * t2 +
                (-p0.x + 3 * p1.x - 3 * p2.x + p3.x) * t3
            );

            const y = 0.5 * (
                (2 * p1.y) +
                (-p0.y + p2.y) * t +
                (2 * p0.y - 5 * p1.y + 4 * p2.y - p3.y) * t2 +
                (-p0.y + 3 * p1.y - 3 * p2.y + p3.y) * t3
            );

            curvePoints.push({ x, y });
        }
    }

    // 确保包含最后一个原始点
    curvePoints.push(points[points.length - 1]);

    return curvePoints;
}

/**
 * 计算样条曲线在指定百分比位置的坐标及切线向量
 * @param {Array} points - 原始坐标点数组，格式 [{x: number, y: number}, ...]
 * @param {number} t - 曲线位置百分比参数，取值范围 [0, 1]
 * @returns {Object} 包含坐标和切线的对象 {x, y, dx, dy}
 */
PlotUtils.prototype.getSplinePointAndTangent = function (points, t) {
    if (points.length < 2) return { x: 0, y: 0, dx: 0, dy: 0 };

    // 参数约束处理（转换为全局位置）
    const totalSegments = points.length - 1;
    let tGlobal = Math.max(0, Math.min(t, 1)) * totalSegments;
    tGlobal = Math.min(tGlobal, totalSegments - 1e-9); // 防止超出最后一个点

    // 创建镜像端点解决边界问题
    const first = points[0], last = points[points.length - 1];
    const extended = [
        { x: 2 * first.x - points[1].x, y: 2 * first.y - points[1].y },
        ...points,
        { x: 2 * last.x - points[points.length - 2].x, y: 2 * last.y - points[points.length - 2].y }
    ];

    // 确定当前曲线段
    const segIndex = Math.floor(tGlobal);
    const localT = tGlobal - segIndex;

    // 获取控制点集（调整索引偏移）
    const p0 = extended[segIndex];
    const p1 = extended[segIndex + 1];
    const p2 = extended[segIndex + 2];
    const p3 = extended[segIndex + 3];

    // 计算坐标点
    const t2 = localT * localT;
    const t3 = t2 * localT;
    const x = 0.5 * (
        (2 * p1.x) +
        (-p0.x + p2.x) * localT +
        (2 * p0.x - 5 * p1.x + 4 * p2.x - p3.x) * t2 +
        (-p0.x + 3 * p1.x - 3 * p2.x + p3.x) * t3
    );
    const y = 0.5 * (
        (2 * p1.y) +
        (-p0.y + p2.y) * localT +
        (2 * p0.y - 5 * p1.y + 4 * p2.y - p3.y) * t2 +
        (-p0.y + 3 * p1.y - 3 * p2.y + p3.y) * t3
    );

    // 计算切线向量
    const dx = 0.5 * (
        (-p0.x + p2.x) +
        2 * (2 * p0.x - 5 * p1.x + 4 * p2.x - p3.x) * localT +
        3 * (-p0.x + 3 * p1.x - 3 * p2.x + p3.x) * t2
    );
    const dy = 0.5 * (
        (-p0.y + p2.y) +
        2 * (2 * p0.y - 5 * p1.y + 4 * p2.y - p3.y) * localT +
        3 * (-p0.y + 3 * p1.y - 3 * p2.y + p3.y) * t2
    );

    return { x, y, dx, dy };
}

export { PlotUtils }
